<!DOCTYPE html>
<html>

<head>
  <meta charset='utf-8'>
  <title>雾化fog</title>
  <link rel="shortcut icon" href="../icon/sparrow.png" />
  <link rel="bookmark" href="../icon/sparrow.png" type="image/x-icon" />
  <link rel="stylesheet" href="../style/common.css" />
  <!-- 对于制作原型或学习，你可以这样使用最新版本： -->
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script src="../lib/webgl-debug.js"></script>
  <script src="../lib/webgl-utils.js"></script>
  <script src="../lib/cuon-matrix.js"></script>
  <script src="../lib/cuon-utils.js"></script>
  <script src="../lib/allx-utils.js"></script>
</head>

<body>
  <h2>gl40 雾化fog</h2>
  <canvas id="canvas" width="400" height="400">
    您的浏览器不支持canvas
  </canvas>

  <script type="text/javascript">
    // 顶点着色器程序2
    let VSHADER_SOURCE = `
        attribute vec4 a_Position; // 顶点坐标
        attribute vec2 a_TexCoord; // 纹理坐标
        attribute vec4 a_Normal; // 法向量

        uniform mat4 u_MvpMatrix; // 模型视图投影矩阵
        uniform mat4 u_ModelMatrix; // 模型矩阵
        uniform mat4 u_NormalMatrix; // 用来变换法向量的矩阵
        uniform vec4 u_Eye; // 视点位置（世界坐标系）

        varying vec3 v_Normal;
        varying vec3 v_Position;
        varying vec2 v_TexCoord;
        varying float v_Dist; // 当前顶点与视点距离

        void main(){
          gl_Position = u_MvpMatrix * a_Position;
          v_Position = vec3(u_ModelMatrix * a_Position); // 计算顶点的世界坐标
          v_Normal = normalize(vec3(u_NormalMatrix * a_Normal)); // 计算变换后的法向量并归一化法向量
          v_TexCoord = a_TexCoord;
          // v_Dist = distance(u_ModelMatrix * a_Position, u_Eye); // 计算当前顶点与视点距离
          v_Dist = gl_Position.w; // 使用视图坐标系的负Z值
        }`;

    // 片元着色器程序2
    let FSHADER_SOURCE = `
        precision mediump float; // 声明片元着色器float的精度（必须指定）

        uniform sampler2D u_Sampler; // 纹理采样器
        uniform vec3 u_LightColor; // 光线颜色
        uniform vec3 u_LightPosition; // 光源位置（世界坐标系）
        uniform vec3 u_AmbientLight; // 环境光颜色
        uniform vec3 u_FogColor; // 雾的颜色
        uniform vec2 u_FogDist; // 雾化的起点和终点

        varying vec3 v_Normal; 
        varying vec3 v_Position;
        varying vec2 v_TexCoord; // 纹理
        varying float v_Dist; // 当前顶点与视点距离

        void main(){
          float fogFactor = clamp((u_FogDist.y - v_Dist)/(u_FogDist.y - u_FogDist.x), 0.0, 1.0); // 计算雾化因子

          vec4 texColor = texture2D(u_Sampler, v_TexCoord); // 纹理颜色
          vec3 color = mix(u_FogColor, vec3(texColor), fogFactor); // 计算雾化后的颜色
          vec3 normal = normalize(v_Normal); // 对插值后的法向量进行归一化（内插后长度不一定为1.0）
          vec3 lightDirection = normalize(u_LightPosition - v_Position); // 计算光线方向并归一化
          float nDotL = max(dot(lightDirection, normal), 0.0); // 计算光线方向和法向量的点积

          vec3 diffuse = u_LightColor * vec3(color) * nDotL; // 计算漫反射光的颜色
          vec3 ambient = u_AmbientLight * color.rgb; // 计算环境反射光颜色

          gl_FragColor = vec4(diffuse + ambient, texColor.a); // 漫反射光 + 环境反射光
        }`;

    const canvas = document.querySelector("#canvas");
    const gl = getWebGLContext(canvas); // 获取canvas，获取webGL绘图上下文
    if (!gl) console.log(Error("无法获得webgl的绘图上下文"));

    if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) console.log(Error("无法初始化着色器")); // 初始化着色器

    let n = initVertexBuffers(gl); // 设置顶点信息（传入attribute变量），返回绘制次数
    if (n < 0) console.log(Error("设置顶点着色器缓冲区信息"));

    const fogColor = new Float32Array([0.137, 0.231, 0.223]);
    const eyePosition = new Float32Array([5, 5, 10]);
    gl.enable(gl.DEPTH_TEST); // 开启深度检测（隐藏面消除）
    gl.clearColor(fogColor[0], fogColor[1], fogColor[2], 1.0); // 设置背景色

    // 获取 uniform 变量位置
    let u_mvpMatrix = gl.getUniformLocation(gl.program, "u_MvpMatrix");
    let u_modelMatrix = gl.getUniformLocation(gl.program, "u_ModelMatrix");
    let u_normalMatrix = gl.getUniformLocation(gl.program, "u_NormalMatrix");
    let u_eye = gl.getUniformLocation(gl.program, "u_Eye");

    let u_lightColor = gl.getUniformLocation(gl.program, "u_LightColor");
    let u_ambientLight = gl.getUniformLocation(gl.program, "u_AmbientLight");
    let u_lightPosition = gl.getUniformLocation(gl.program, "u_LightPosition");
    let u_fogColor = gl.getUniformLocation(gl.program, "u_FogColor");
    let u_fogDist = gl.getUniformLocation(gl.program, "u_FogDist");

    gl.uniform3f(u_lightColor, 1.0, 1.0, 1.0); // 传入 白色光线
    gl.uniform3f(u_ambientLight, 0.1, 0.1, 0.1); // 传入 环境光颜色 更真实
    gl.uniform3f(u_lightPosition, 20.0, 20.0, 40.0); // 传入 点光源位置（世界坐标）
    gl.uniform3fv(u_fogColor, fogColor); // 传入 雾的颜色
    gl.uniform2f(u_fogDist, 0.0, 30.0); // 传入 雾化的起点和终点
    gl.uniform3fv(u_eye, eyePosition); // 传入 视点位置（世界坐标系）

    let g_modelMatrix = new Matrix4(); // 全局 模型矩阵
    let g_viewProjMatrix = new Matrix4(); // 创建视图投影矩阵
    let g_mvpMatrix = new Matrix4(); // 全局 模型视图投影矩阵
    let g_normalMatrix = new Matrix4(); // 全局 魔法矩阵 

    let currentAngle = [0.0, 0.0]; // [X轴旋转角度, Y轴旋转角度]

    if (!initTextures(gl, n)) console.log(Error("无法配置纹理")); // 配置纹理

    function tick() {
      draw(gl, n, g_viewProjMatrix, u_mvpMatrix, currentAngle);
      requestAnimationFrame(tick, canvas);
    }
    tick();

    function initVertexBuffers(gl) {
      var vertices = new Float32Array([
        1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, 1.0, // v0-v1-v2-v3 front
        1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, // v0-v3-v4-v5 right
        1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, // v0-v5-v6-v1 up
        -1.0, 1.0, 1.0, -1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, // v1-v6-v7-v2 left
        -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, -1.0, -1.0, 1.0, // v7-v4-v3-v2 down
        1.0, -1.0, -1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0 // v4-v7-v6-v5 back
      ]);

      var texCoords = new Float32Array([ // Texture coordinates
        1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v1-v2-v3 front
        0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, // v0-v3-v4-v5 right
        1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // v0-v5-v6-v1 up
        1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v1-v6-v7-v2 left
        0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // v7-v4-v3-v2 down
        0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 // v4-v7-v6-v5 back
      ]);

      var normals = new Float32Array([ // 每一个面的法向量
        0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, // v0-v1-v2-v3 front
        1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, // v0-v3-v4-v5 right
        0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v5-v6-v1 up
        -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, // v1-v6-v7-v2 left
        0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, // v7-v4-v3-v2 down
        0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0, 0.0, 0.0, -1.0 // v4-v7-v6-v5 back
      ]);

      var indices = new Uint8Array([ // Indices of the vertices
        0, 1, 2, 0, 2, 3, // front
        4, 5, 6, 4, 6, 7, // right
        8, 9, 10, 8, 10, 11, // up
        12, 13, 14, 12, 14, 15, // left
        16, 17, 18, 16, 18, 19, // down
        20, 21, 22, 20, 22, 23 // back
      ]);

      if (!initArrayBuffer(gl, "a_Position", vertices, 3, gl.FLOAT)) return -1; // 创建缓冲区并赋值attribute
      if (!initArrayBuffer(gl, "a_TexCoord", texCoords, 2, gl.FLOAT)) return -1; // 创建缓冲区并赋值attribute
      if (!initArrayBuffer(gl, "a_Normal", normals, 3, gl.FLOAT)) return -1; // 创建缓冲区并赋值attribute

      let indexBuffer = gl.createBuffer();
      if (!indexBuffer) {
        console.log(Error('Failed to create index buffer'));
        return -1;
      }
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW); // 写ELEMENT_ARRAY_BUFFER入缓冲区

      return indices.length;
    }

    function initTextures(gl, n) {
      let texture = gl.createTexture(); // 创建纹理对象 // 省略错误处理
      let u_sampler = gl.getUniformLocation(gl.program, "u_Sampler"); // 获取 u_Sampler 存储位置 // 省略错误处理

      let image = new Image(); // 创建image图像
      image.onload = () => loadTexture(gl, n, texture, u_sampler, image) // 注册图像加载事件的响应函数
        .then(() => addListener()) // 添加事件监听
        .then(() => draw(gl, n, g_viewProjMatrix, u_mvpMatrix, currentAngle)); // 绘制
      image.src = "../texture/texture1.jpg"; // 浏览器加载图像

      return true;
    }

    function loadTexture(gl, n, texture, u_sampler, image) {
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1); // 对纹理图像进行y轴翻转
      gl.activeTexture(gl.TEXTURE0); // 开启0号纹理单元
      gl.bindTexture(gl.TEXTURE_2D, texture); // 向target绑定纹理对象

      // 配置纹理参数
      // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

      //配置纹理参数
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); // 配置纹理对象

      gl.uniform1i(u_sampler, 0); // 将0号纹理传递给着色器 // 省略错误处理

      return new Promise((resolve, reject) => resolve());
    }

    function addListener() {
      console.log("开始添加事件");

      let drag_flag = false; // 是否在拖动
      let lastX = -1; // 鼠标最后的位置
      let lastY = -1; // 鼠标最后的位置

      canvas.addEventListener("mousedown", event => { // 按下鼠标
        let x = event.clientX;
        let y = event.clientY;
        // 限制在 canvas 内拖动
        let rect = event.target.getBoundingClientRect();

        if (rect.left <= x && x < rect.right && rect.top <= y && rect.bottom > y) {
          lastX = x;
          lastY = y;
          drag_flag = true;
        }
      });

      canvas.addEventListener("mouseup", event => drag_flag = false); // 松开鼠标

      canvas.addEventListener("mousemove", event => { // 移动鼠标
        let x = event.clientX;
        let y = event.clientY;
        if (drag_flag) {
          let factor = 100 / canvas.height; // 旋转因子
          let dx = factor * (x - lastX);
          let dy = factor * (y - lastY);
          currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy, 90.0), -90.0); // 控制角度
          // currentAngle36[0] = currentAngle36[0] + dy;
          currentAngle[1] = currentAngle[1] + dx;

          // draw36(gl36, n36, g_viewProjMatrix36, u_mvpMatrix36, currentAngle36);
        }
        lastX = x;
        lastY = y;
      });

      return new Promise((resolve, reject) => resolve());
    }

    function draw(gl, n, g_viewProjMatrix, u_mvpMatrix, currentAngle) {
      g_mvpMatrix.setPerspective(30.0, 1, 1.0, 100.0); // 垂直视角、近裁剪面宽高比、近裁剪面位置、远裁剪面位置
      g_mvpMatrix.lookAt(eyePosition[0], eyePosition[1], eyePosition[2], 0, 0, 0, 0, 1, 0); // 视点、视线（被观察对象位置）、上方向

      g_modelMatrix.setRotate(currentAngle[0], 1, 0, 0); // X轴
      g_modelMatrix.rotate(currentAngle[1], 0, 1, 0); // Y轴
      gl.uniformMatrix4fv(u_modelMatrix, false, g_modelMatrix.elements); // 传入 模型矩阵

      g_normalMatrix.setInverseOf(g_modelMatrix); // 取模型矩阵逆矩阵
      g_normalMatrix.transpose(); // 取转置矩阵
      gl.uniformMatrix4fv(u_normalMatrix, false, g_normalMatrix.elements); // 传入 魔法矩阵

      g_mvpMatrix.multiply(g_modelMatrix);
      gl.uniformMatrix4fv(u_mvpMatrix, false, g_mvpMatrix.elements);

      gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // 清空颜色及深度缓冲区
      gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0); // 绘制
    }
  </script>

</body>

</html>